1 Read

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Read
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

## Read files
file <- "Date Complete M1 v.13 siPPGGSRamilaza.sav"

# setwd(folder)
Data <- rio::import(file)

2 Make data frame

Data %>%
  dplyr::select(-Nume) %>%
    DT::datatable(
      extensions = 'Buttons',
      options = list(pageLength = 10,
                     scrollX='500px',
                     dom = 'Bfrtip',
                     buttons = c('excel', "csv")))

2.1 Exclude Protocol 8 (mother)

Data <- 
  Data %>%
  filter(P != 8)

3 Define Functions

## Func t test si boxplot simplu
func_t_box <- function(df, ind, pre_var, post_var, facet = FALSE, xlab = ""){  
  if(facet){
    facet <- "Protocol"
  }else{
    facet <- NULL
  }
  
  df_modif <-
    df %>%
    select(ind, P, pre_var, post_var) %>% 
    tidyr::drop_na() %>%
    gather(pre_var, post_var, key = "PrePost", value = "value") %>% 
    mutate_at(vars(c(1, 2)), funs(as.factor)) %>% 
    mutate(PrePost = factor(PrePost, levels = c(pre_var, post_var))) 
  
  if(!is.null(facet)){
    df_modif <-
      df_modif %>%
      group_by(P) %>%
      mutate(Protocol = paste0("Protocol = ", P, ", n = ", n()))
  }
  
  stat_comp <-
    df_modif %>% 
    do(tidy(t.test(.$value ~ .$PrePost,
                   paired = TRUE,
                   data=.)))
  
  plot <- 
    ggpubr::ggpaired(df_modif, x = "PrePost", y = "value", id = ind, 
                     color = "PrePost", line.color = "gray", line.size = 0.4,
                     palette = c("#00AFBB", "#FC4E07"), legend = "none",
                     facet.by = facet, ncol = 3, 
                     xlab = xlab) +
    stat_summary(fun.data = mean_se,  colour = "darkred") +
    ggpubr::stat_compare_means(method = "t.test", paired = TRUE, label.x = as.numeric(df_modif$PrePost)-0.4, label.y = max(df_modif$value)+1) + 
    ggpubr::stat_compare_means(method = "t.test", paired = TRUE, label = "p.signif", comparisons = list(c(pre_var, post_var)))
  
  print(stat_comp)
  cat("\n")                      
  print(plot)
  cat("\n")
  plot.new()                     # Need this workaround for interleaving tables and plots in R Markdown, within loop
  dev.off()
}
heat_cor_plotly <- function(df, x_vars = NULL, y_vars = NULL, low_color = "cyan",  high_color = "red",  ...){   
  # inherit type = c("pearson","spearman") from Hmisc::rcorr() 
  library(ggplot2)
  library(plotly)
  library(reshape2)
  library(Hmisc)
  
  # use all numeric columns only, print message if non-numeric are found
  numeric_cols <- unlist(lapply(df, is.numeric))
  if(!all(numeric_cols)) message("Warning: Non-numeric columns were excluded!")
  df <- df[, numeric_cols]
  
  df_mat <- as.matrix(df)
  rt <- Hmisc::rcorr(df_mat, ...)
  
  # extract correlations, p-values and merge into another dataframe
  mtlr <- reshape2::melt(rt$r, value.name = "Correlation")
  mtlp <- reshape2::melt(rt$P, value.name = "P-Value")
  
  mtl <- merge(mtlr, mtlp)
  
  # give possibility to prune the correlation matrix
  if(!is.null(x_vars)){
    mtl <- mtl[(mtl$Var1 %in% x_vars), ]
  }
  if(!is.null(x_vars)){
    mtl <- mtl[(mtl$Var2 %in% y_vars), ]
  }
  
  # want to avoid scientific notetion, but this doesnt work as numeric
  # mtl$Correlation <- as.numeric(format(mtl$Correlation, digits = 4, scientific = FALSE))  # doesnt work
  # mtl$`P-Value` <- as.numeric(format(mtl$`P-Value`, digits = 4, scientific = FALSE)) 
  options(scipen = 999)
  mtl$Correlation <- round(mtl$Correlation, 3)
  mtl$`P-Value` <- round(mtl$`P-Value`, 3)

  gx <-
    ggplot2::ggplot(mtl, 
           aes(Var1, Var2, 
               fill = Correlation,  
               text = paste("P-val = ", `P-Value`))) +
    ggplot2::geom_tile() + 
    ggplot2::scale_fill_gradient(low = low_color,  high = high_color, limits = c(-1, 1), breaks = c(-1, -.5, 0, .5, 1)) +
    ggplot2::theme_minimal() +
    {if(any(nchar(names(df)) > 6)) ggplot2::theme(axis.text.x = element_text(angle = 90, hjust = 1))}  # vertical x axis labels if lenghty
  plotly::ggplotly(gx)  
}

4 Plot Age

## Dodged Bar plot of Age and Gender
Data  %>%
  mutate(Varta_categ = cut(Varsta, 
                           breaks=c(-Inf, 25, 30, 35, 40, 45, 50, 55, 60, Inf), 
                           labels=c("<25","25-29","30-34", "35-39", "40-44", "45-49", "50-54", "55-59", "60>"), 
                           right = FALSE)) %>%  
  mutate(Varsta = as.factor(Varsta),
         Gen = as.factor(as.character(Gen))) %>%
  mutate(Gen = forcats::fct_recode(Gen, "femin" = "1", "masculin" = "2")) %>%
  dplyr::count(Varta_categ, Gen, .drop = FALSE) %>%         # Group by, then count number in each group (dont drop 0 counts)
  mutate(pct = prop.table(n)) %>%                           # Calculate percent within each var
    ggplot(aes(x = Varta_categ, y = pct, fill = Gen, label = scales::percent(pct))) + 
      geom_col(position = position_dodge(preserve = "single"), stat = "identity",) +    # Don't drop zero count
      geom_text(position = position_dodge(width = .9),      # move to center of bars
                vjust = -0.5,                               # nudge above top of bar
                size = 3) + 
      scale_y_continuous(labels = scales::percent) +
      ggtitle("") +
      xlab("Varsta") + ylab("Percentage %") + 
      guides(fill = guide_legend(title = "Gen", ncol = 1)) + 
      scale_fill_grey(start = 0.8, end = 0.2, na.value = "red", aesthetics = "fill") +
      theme(legend.position = "right", legend.direction = "vertical", 
            legend.justification = c(0, 1), panel.border = element_rect(fill = NA, colour = "black"))

4.1 By Protocol

## Dodged Bar plot of Age and Gender by Protocol
Data  %>%
  mutate(Varta_categ = cut(Varsta, 
                           breaks=c(-Inf, 25, 30, 35, 40, 45, 50, 55, 60, Inf), 
                           labels=c("<25","25-29","30-34", "35-39", "40-44", "45-49", "50-54", "55-59", "60>"), 
                           right = FALSE)) %>%  
  mutate(Varsta = as.factor(Varsta),
         Gen = as.factor(as.character(Gen))) %>%
  mutate(Gen = forcats::fct_recode(Gen, "femin" = "1", "masculin" = "2")) %>%
  group_by(P) %>%
  dplyr::count(Varta_categ, Gen, .drop = FALSE) %>%         # Group by, then count number in each group (dont drop 0 counts)
  mutate(pct = prop.table(n)) %>%                           # Calculate percent within each var
    ggplot(aes(x = Varta_categ, y = pct, fill = Gen, label = scales::percent(pct))) +
      facet_wrap(~P, scales = "free", ncol = 1) +
      geom_col(position = position_dodge(preserve = "single"), stat = "identity",) +    # Don't drop zero count
      geom_text(position = position_dodge(width = .9),      # move to center of bars
                vjust = -0.5,                               # nudge above top of bar
                size = 3) + 
      scale_y_continuous(labels = scales::percent) +
      ggtitle("") +
      xlab("Varsta") + ylab("Percentage %") + 
      guides(fill = guide_legend(title = "Gen", ncol = 1)) + 
      scale_fill_grey(start = 0.8, end = 0.2, na.value = "red", aesthetics = "fill") +
      theme(legend.position = "right", legend.direction = "vertical", 
            legend.justification = c(0, 1), panel.border = element_rect(fill = NA, colour = "black"))

## Pie chart
Data  %>%
  mutate(Gen = as.factor(as.character(Gen))) %>%
  mutate(Gen = forcats::fct_recode(Gen, "femin" = "1", "masculin" = "2")) %>%
  group_by(Gen) %>%
  dplyr::summarise(counts = n()) %>%
  mutate(prop = round(counts*100/sum(counts), 1),
         lab.ypos = cumsum(prop) - .5*prop,
         Percent = paste0(prop, " %")) %>% 
  ggpubr::ggpie(x = "prop", label = "Percent",
                fill = "Gen", color = "white", 
                lab.pos = "in", lab.font = list(color = "white"),
                palette = "grey")

5 Analyses

5.1 Simple before-after analyses with t test

## Simple before-after analyses with t test
cat("#### VAS Stress")

5.1.0.1 VAS Stress

func_t_box(Data, ind = "ID", "Stres_pre", "Stres_post", facet = FALSE) 

NANA

null device 1

## Simple before-after analyses with t test
cat("#### VAS Stress")

5.1.0.2 VAS Stress

func_t_box(Data, ind = "ID", "Stres_pre", "Stres_post", facet = TRUE) 

NANA

null device 1

5.2 Correlations: Anotimpuri - Personality (without P6, P7)

dateplot_anopers <- Data[, c("P", "Primavara", "Vara", "Toamna", "Iarna")]
dateplot_anopers <- cbind(dateplot_anopers, Data[, 87:121])
dateplot_anopers <- subset(dateplot_anopers, P!=6 & P!=7)

COR <- Hmisc::rcorr(as.matrix(dateplot_anopers[, -1]))   
M <- COR$r[1:4, ]
P_MAT <- COR$P[1:4, ]
corrplot::corrplot(M, type = "upper", p.mat = P_MAT, sig.level = 0.05, insig = "blank", tl.col = "black", tl.cex = .7, cl.pos = "b", tl.srt = 45)

Error in data.frame(…, check.names = FALSE) : arguments imply differing number of rows: 150, 146

5.3 Correlations: Anotimpuri - Calitate Amintiri (without P6, P7)

dateplot1 <- Data[, c("P", "Primavara", "Vara", "Toamna", "Iarna", "Media_s1", "Media_s2", "Media_s3",  "SocDih_Part",  "SocDih_FamN",  "SocDih_FamInd",  "SocDih_Priet",  "SocDih_Amici",  "SocDih_Necun",  "SocDih_Antag",  "SocDih_TotAprop",  "SocDih_TotNeaprop", "STAI_T")] 
names(dateplot1) <- c("P", "Primavara", "Vara", "Toamna", "Iarna", "S1- Valenta", "S2 - Vividness", "S3 - Relevanta",  "Partener",  "Familie nucleu",  "Familie extinsa",  "Prieteni",  "Amici",  "Necunoscuti",  "Antagonisti",  "Toti Apropiatii",  "Toti Neapropiatii", "STAI_T")
dateplot1 <- subset(dateplot1, P!=6 & P!=7)

COR <- Hmisc::rcorr(as.matrix(dateplot1[,-1]))   
M <- COR$r
P_MAT <- COR$P
corrplot::corrplot(M, method = "number", type = "upper", p.mat = P_MAT, sig.level = 0.05, insig = "blank", tl.col = "black", tl.cex = .9, tl.srt = 45)  

Error in data.frame(…, check.names = FALSE) : arguments imply differing number of rows: 153, 136

heat_cor_plotly(dateplot1[,-1])

5.4 Correlations: Personality - Qualities of Memories (without P6, P7)

dateplot2 <- Data[, c(24, 40, 56, 87:121, 126)] 
names(dateplot2)[1:3] <- c("S1- Valenta", "S2 - Vividness", "S3 - Relevanta")

COR <- Hmisc::rcorr(as.matrix(dateplot2))   
M <- COR$r
P_MAT <- COR$P
corrplot::corrplot(M, type = "upper", p.mat = P_MAT, sig.level = 0.05, insig = "blank", tl.col = "black", tl.cex = .7, cl.pos = "b", tl.srt = 45)

Error in data.frame(…, check.names = FALSE) : arguments imply differing number of rows: 780, 741

heat_cor_plotly(dateplot2, x_vars = names(dateplot2)[1:3], y_vars = names(dateplot2)[-(1:3)])

5.5 Correlations: Social - Personality

dateplot3 <- Data[, c(131:139, 87:121)]
names(dateplot3)[1:9] <- c("Partener",  "Familie nucleu",  "Familie extinsa",  "Prieteni",  "Amici",  "Necunoscuti",  "Antagonisti",  "Toti Apropiatii",  "Toti Neapropiatii")

COR <- Hmisc::rcorr(as.matrix(dateplot3))   
M <- COR$r
P_MAT <- COR$P
corrplot::corrplot(M, type = "upper", p.mat = P_MAT, sig.level = 0.05, insig = "blank", tl.col = "black", tl.cex = .7, cl.pos = "b", tl.srt = 45)

Error in data.frame(…, check.names = FALSE) : arguments imply differing number of rows: 990, 946

heat_cor_plotly(dateplot3, x_vars = names(dateplot3)[1:9], y_vars = names(dateplot3)[-(1:9)])

6 Social

# names(Data[str_detect(colnames(Data), fixed("SocDih", ignore_case=TRUE))])
Data_soc <-
  Data %>%
  dplyr::select("ID", "P", starts_with("SocDih")) %>%
  haven::zap_formats(.) %>%                # to not get warning  
  haven::zap_labels(.) %>%                 # "attributes are not identical across measure variables"
  haven::zap_widths(.) %>%                 # on gather() because of SPSS
  dplyr::rename_all(list(~stringr::str_replace(., "SocDih_", ""))) %>% 
  gather(key = "Variable", value = "Value", -ID, -P) 
  
# Create a custom color scale for all ASCQ graphs
library(RColorBrewer)
myColors <- brewer.pal(10,"Set1")
names(myColors) <- levels(Data_soc$Variable)
colScale <- scale_colour_manual(name = "Variable", values = myColors)

# Plot
ggpubr::ggviolin(data = Data_soc, x = "Variable", y = "Value", fill = "Variable",
      add = "boxplot", add.params = list(fill = "white"),
      xlab = "", legend = "none") +
  colScale +                                                   # color scale here keep consistency of color with factor level
  stat_summary(fun.data = mean_se,  colour = "darkred") +         
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

# Plot faceted by Protocol
p <- 
  ggpubr::ggviolin(data = Data_soc, x = "Variable", y = "Value", fill = "Variable",
        add = "boxplot", add.params = list(fill = "white"),
        xlab = "", legend = "none") +
         
    colScale +                                                   # color scale here keep consistency of color with factor level
    stat_summary(fun.data = mean_se,  colour = "darkred") +         
    theme(axis.text.x = element_text(angle = 90, hjust = 1))

ggpubr::facet(p, facet.by = "P", ncol = 1, scales = "free_x")

6.1 Social - this doesnt make sense

# PerformanceAnalytics::chart.Correlation(Data[, c(8, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140)])

# Example of Simpsons Paradox
coplot(DifStres ~ SocDih_Amici | Media_s1,
       data = Data,
       rows = 1,
     panel = function(x, y, ...) {
          panel.smooth(x, y, span = .8, iter = 5,...)
          abline(lm(y ~ x), col = "blue")})

7 Varsta Amint - P1,P2,P3

Data_P1P2P3 <- 
  Data %>%
  filter(P %in% c("1", "2", "3")) %>%
  mutate(Med_amintvarsta = as.numeric(as.character(Med_amintvarsta)),
         Dif_Med_amintvarsta = Varsta - Med_amintvarsta)

PerformanceAnalytics::chart.Correlation(Data_P1P2P3[, c(8, 85, 122, 148)])


coplot(DifStres ~ Dif_Med_amintvarsta | Media_s1,
       data = Data_P1P2P3,
       rows = 1,
     panel = function(x, y, ...) {
          panel.smooth(x, y, span = .8, iter = 5,...)
          abline(lm(y ~ x), col = "blue")})

8 Protocol 3 - Social

Data_P3 <- 
  Data %>%
  filter(P == "3")

PerformanceAnalytics::chart.Correlation(Data_P3[, c(8, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140)])


# coplot(SocDih_Amici ~ DifStres | Media_s1, 
#        data = Data,
#        columns = 3,
#      panel = function(x, y, ...) {
#           panel.smooth(x, y, span = .8, iter = 5,...)
#           abline(lm(y ~ x), col = "blue") } )

9 Protocol 3 - Varsta Amint

Data_P3 <- 
  Data %>%
  filter(P == "3") %>%
  mutate(Med_amintvarsta = as.numeric(as.character(Med_amintvarsta)),
         Dif_Med_amintvarsta = Varsta - Med_amintvarsta)

PerformanceAnalytics::chart.Correlation(Data_P3[, c(8, 85, 122, 148)])



10 Session Info

R version 4.2.2 (2022-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 8.1 x64 (build 9600)

Matrix products: default

locale:
[1] LC_COLLATE=Romanian_Romania.1252  LC_CTYPE=Romanian_Romania.1252    LC_MONETARY=Romanian_Romania.1252
[4] LC_NUMERIC=C                      LC_TIME=Romanian_Romania.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] RColorBrewer_1.1-3         Hmisc_5.0-1                reshape2_1.4.4             plotly_4.10.1             
 [5] rio_0.5.29                 scales_1.2.1               ggpubr_0.6.0               rstatix_0.7.2             
 [9] broom_1.0.4                PerformanceAnalytics_2.0.4 xts_0.13.0                 zoo_1.8-11                
[13] psych_2.3.3                plyr_1.8.8                 lubridate_1.9.2            forcats_1.0.0             
[17] stringr_1.5.0              dplyr_1.1.1                purrr_1.0.1                readr_2.1.4               
[21] tidyr_1.3.0                tibble_3.2.1               ggplot2_3.4.2              tidyverse_2.0.0           
[25] papaja_0.1.1               tinylabels_0.2.3           pacman_0.5.3              

loaded via a namespace (and not attached):
 [1] colorspace_2.1-0    ggsignif_0.6.4      ellipsis_0.3.2      rprojroot_2.0.3     estimability_1.4.1  htmlTable_2.4.1    
 [7] parameters_0.20.3   base64enc_0.1-3     fs_1.6.1            rstudioapi_0.14     farver_2.1.1        DT_0.27            
[13] fansi_1.0.4         mvtnorm_1.1-3       mnormt_2.1.1        cachem_1.0.6        knitr_1.42          Formula_1.2-5      
[19] jsonlite_1.8.4      cluster_2.1.4       effectsize_0.8.3    BiocManager_1.30.20 compiler_4.2.2      httr_1.4.5         
[25] emmeans_1.8.5       backports_1.4.1     fastmap_1.1.0       lazyeval_0.2.2      cli_3.6.1           htmltools_0.5.5    
[31] tools_4.2.2         coda_0.19-4         gtable_0.3.3        glue_1.6.2          Rcpp_1.0.10         carData_3.0-5      
[37] jquerylib_0.1.4     cellranger_1.1.0    vctrs_0.6.2         nlme_3.1-160        crosstalk_1.2.0     conflicted_1.2.0   
[43] insight_0.19.1      xfun_0.38           openxlsx_4.2.5.2    timechange_0.2.0    lifecycle_1.0.3     limonaid_0.1.5     
[49] hms_1.1.3           parallel_4.2.2      yaml_2.3.7          curl_5.0.0          memoise_2.0.1       gridExtra_2.3      
[55] sass_0.4.5          rpart_4.1.19        reshape_0.8.9       stringi_1.7.12      bayestestR_0.13.1   corrplot_0.92      
[61] checkmate_2.1.0     zip_2.2.2           rlang_1.1.0         pkgconfig_2.0.3     evaluate_0.20       lattice_0.20-45    
[67] labeling_0.4.2      htmlwidgets_1.6.2   tidyselect_1.2.0    here_1.0.1          GGally_2.1.2        magrittr_2.0.3     
[73] R6_2.5.1            magick_2.7.4        generics_0.1.3      pillar_1.9.0        haven_2.5.2         foreign_0.8-83     
[79] withr_2.5.0         datawizard_0.7.1    abind_1.4-5         nnet_7.3-18         crayon_1.5.2        car_3.1-1          
[85] utf8_1.2.3          tzdb_0.3.0          rmarkdown_2.21      grid_4.2.2          readxl_1.4.2        data.table_1.14.8  
[91] digest_0.6.31       xtable_1.8-4        munsell_0.5.0       viridisLite_0.4.1   bslib_0.4.2         quadprog_1.5-8     
 

A work by Claudiu Papasteri

 

LS0tDQp0aXRsZTogIjxicj4gR2VuZXJhbCBQbG90cyBmb3IgTS4xLiAoQXV0b2Jpb2dyYXBoaWNhbCBNZW1vcmllcykiIA0Kc3VidGl0bGU6ICJJbml0aWFsIERhdGFzZXQiDQphdXRob3I6ICI8YnI+IENsYXVkaXUgUGFwYXN0ZXJpIg0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJW0gJVknKWAiDQpvdXRwdXQ6IA0KICAgIGh0bWxfbm90ZWJvb2s6DQogICAgICAgICAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICAgICAgICAgIHRvYzogdHJ1ZQ0KICAgICAgICAgICAgdG9jX2RlcHRoOiAyDQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICAgICAgICAgIHRoZW1lOiBzcGFjZWxhYg0KICAgICAgICAgICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgICAgICAgICAgZm9udC1mYW1pbHk6IEFyaWFsDQogICAgICAgICAgICBmaWdfd2lkdGg6IDEwDQogICAgICAgICAgICBmaWdfaGVpZ2h0OiA5DQogICAgIyB3b3JkX2RvY3VtZW50ICAgICAgICANCiAgICAjIHBkZl9kb2N1bWVudDogDQogICAgICAgICAgICAjIHRvYzogdHJ1ZQ0KICAgICAgICAgICAgIyB0b2NfZGVwdGg6IDINCiAgICAgICAgICAgICMgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgICAgICAgICAjIGZvbnRzaXplOiAxMXB0DQogICAgICAgICAgICAjIGdlb21ldHJ5OiBtYXJnaW49MWluDQogICAgICAgICAgICAjIGZpZ193aWR0aDogNw0KICAgICAgICAgICAgIyBmaWdfaGVpZ2h0OiA2DQogICAgICAgICAgICAjIGZpZ19jYXB0aW9uOiB0cnVlDQogICAgIyBnaXRodWJfZG9jdW1lbnQ6IA0KICAgICAgICAgICAgIyB0b2M6IHRydWUNCiAgICAgICAgICAgICMgdG9jX2RlcHRoOiAyDQogICAgICAgICAgICAjIGh0bWxfcHJldmlldzogZmFsc2UNCiAgICAgICAgICAgICMgZmlnX3dpZHRoOiA1DQogICAgICAgICAgICAjIGZpZ19oZWlnaHQ6IDUNCiAgICAgICAgICAgICMgZGV2OiBqcGVnDQotLS0NCg0KDQo8IS0tIFNldHVwIC0tPg0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBraW50ciBvcHRpb25zDQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGNvbW1lbnQgPSAiIyIsDQogIGNvbGxhcHNlID0gVFJVRSwNCiAgZWNobyA9IFRSVUUsIA0KICB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZXJyb3IgPSBGQUxTRSwNCiAgY2FjaGUgPSBUUlVFICAgICAgICMgZWNobyA9IEZhbHNlIGZvciBnaXRodWJfZG9jdW1lbnQsIGJ1dCB3aWxsIGJlIGZvbGRlZCBpbiBodG1sX25vdGVib29rDQopDQoNCiMgR2VuZXJhbCBSIG9wdGlvbnMgYW5kIGluZm8NCnNldC5zZWVkKDExMSkgICAgICAgICAgICAgICAjIGluIGNhc2Ugd2UgdXNlIHJhbmRvbWl6ZWQgcHJvY2VkdXJlcyAgICAgICANCm9wdGlvbnMoc2NpcGVuID0gOTk5KSAgICAgICAjIHBvc2l0aXZlIHZhbHVlcyBiaWFzIHRvd2FyZHMgZml4ZWQgYW5kIG5lZ2F0aXZlIHRvd2FyZHMgc2NpZW50aWZpYyBub3RhdGlvbg0KDQojIExvYWQgcGFja2FnZXMNCmlmICghcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpDQpwYWNrYWdlcyA8LSBjKA0KICAicGFwYWphIiwNCiAgInRpZHl2ZXJzZSIsICJwbHlyIiwgICAgICANCiAgInBzeWNoIiwgIlBlcmZvcm1hbmNlQW5hbHl0aWNzIiwgICAgICAgICAgDQogICJicm9vbSIsICJyc3RhdGl4IiwNCiAgInN1bW1hcnl0b29scyIsICJ0YWRhYXRvb2xib3giLCAgICAgICAgICAgDQogICJnZ3Bsb3QyIiwgImdncHViciIsICJzY2FsZXMiLCAgICAgICAgDQogICJyaW8iDQogICMgLCAuLi4NCikNCmlmICghcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpDQpwYWNtYW46OnBfbG9hZChjaGFyID0gcGFja2FnZXMpDQoNCiMgVGhlbWVzIGZvciBnZ3Bsb3QyIHBsb3RpbmcgKGhlcmUgdXNlZCBBUEEgc3R5bGUpDQp0aGVtZV9zZXQodGhlbWVfYXBhKCkpDQoNCiMgVGFibGVzIGtuaXR0aW5nIHRvIFdvcmQNCmRvYy50eXBlIDwtIGtuaXRyOjpvcHRzX2tuaXQkZ2V0KCdybWFya2Rvd24ucGFuZG9jLnRvJykgICMgdGhlbiBmb3JtYXQgdGFibGVzIHVzaW5nIGFuIGlmIHN0YXRlbWVudCBsaWtlOg0KIyBpZiAoZG9jLnR5cGUgPT0gImRvY3giKSB7IHBhbmRlcjo6cGFuZGVyKGRmKSB9IGVsc2UgeyBrbml0cjo6a2FibGUoZGYpIH0NCg0KIyBTZXQgd2QgZm9yIE5vdGVib29rDQpmb2xkZXIgPC0gIkM6L1VzZXJzL01paGFpL0Rlc2t0b3AvUiBOb3RlYm9va3Mvbm90ZWJvb2tzL00uMS4gR2VuZXJhbCINCiMga25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBub3JtYWxpemVQYXRoKGZvbGRlcikpDQpgYGANCg0KDQoNCg0KDQo8IS0tIFJlcG9ydCAtLT4NCg0KDQojIFJlYWQNCg0KYGBge3IgcmVkX2NsZWFuX3JlY29kZV9tZXJnZSwgcmVzdWx0cz0naGlkZSd9DQojfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fg0KIyBSZWFkDQojfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fg0KDQojIyBSZWFkIGZpbGVzDQpmaWxlIDwtICJEYXRlIENvbXBsZXRlIE0xIHYuMTMgc2lQUEdHU1JhbWlsYXphLnNhdiINCg0KIyBzZXR3ZChmb2xkZXIpDQpEYXRhIDwtIHJpbzo6aW1wb3J0KGZpbGUpDQpgYGANCg0KDQojIE1ha2UgZGF0YSBmcmFtZQ0KDQpgYGB7ciBkZl9leGNlbH0NCkRhdGEgJT4lDQogIGRwbHlyOjpzZWxlY3QoLU51bWUpICU+JQ0KICAgIERUOjpkYXRhdGFibGUoDQogICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLA0KICAgICAgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwLA0KICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWD0nNTAwcHgnLA0KICAgICAgICAgICAgICAgICAgICAgZG9tID0gJ0JmcnRpcCcsDQogICAgICAgICAgICAgICAgICAgICBidXR0b25zID0gYygnZXhjZWwnLCAiY3N2IikpKQ0KYGBgDQoNCg0KIyMgRXhjbHVkZSBQcm90b2NvbCA4IChtb3RoZXIpDQoNCmBgYHtyIGRmX2ZpbHRlcmVkfQ0KRGF0YSA8LSANCiAgRGF0YSAlPiUNCiAgZmlsdGVyKFAgIT0gOCkNCmBgYA0KDQoNCiMgRGVmaW5lIEZ1bmN0aW9ucyANCg0KYGBge3IgZGVmX2Z1bmNfdHRlc3QsIGhpZGU9VFJVRSwgcmVzdWx0cz0nYXNpcyd9DQojIyBGdW5jIHQgdGVzdCBzaSBib3hwbG90IHNpbXBsdQ0KZnVuY190X2JveCA8LSBmdW5jdGlvbihkZiwgaW5kLCBwcmVfdmFyLCBwb3N0X3ZhciwgZmFjZXQgPSBGQUxTRSwgeGxhYiA9ICIiKXsgIA0KICBpZihmYWNldCl7DQogICAgZmFjZXQgPC0gIlByb3RvY29sIg0KICB9ZWxzZXsNCiAgICBmYWNldCA8LSBOVUxMDQogIH0NCiAgDQogIGRmX21vZGlmIDwtDQogICAgZGYgJT4lDQogICAgc2VsZWN0KGluZCwgUCwgcHJlX3ZhciwgcG9zdF92YXIpICU+JSANCiAgICB0aWR5cjo6ZHJvcF9uYSgpICU+JQ0KICAgIGdhdGhlcihwcmVfdmFyLCBwb3N0X3Zhciwga2V5ID0gIlByZVBvc3QiLCB2YWx1ZSA9ICJ2YWx1ZSIpICU+JSANCiAgICBtdXRhdGVfYXQodmFycyhjKDEsIDIpKSwgZnVucyhhcy5mYWN0b3IpKSAlPiUgDQogICAgbXV0YXRlKFByZVBvc3QgPSBmYWN0b3IoUHJlUG9zdCwgbGV2ZWxzID0gYyhwcmVfdmFyLCBwb3N0X3ZhcikpKSANCiAgDQogIGlmKCFpcy5udWxsKGZhY2V0KSl7DQogICAgZGZfbW9kaWYgPC0NCiAgICAgIGRmX21vZGlmICU+JQ0KICAgICAgZ3JvdXBfYnkoUCkgJT4lDQogICAgICBtdXRhdGUoUHJvdG9jb2wgPSBwYXN0ZTAoIlByb3RvY29sID0gIiwgUCwgIiwgbiA9ICIsIG4oKSkpDQogIH0NCiAgDQogIHN0YXRfY29tcCA8LQ0KICAgIGRmX21vZGlmICU+JSANCiAgICBkbyh0aWR5KHQudGVzdCguJHZhbHVlIH4gLiRQcmVQb3N0LA0KICAgICAgICAgICAgICAgICAgIHBhaXJlZCA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgZGF0YT0uKSkpDQogIA0KICBwbG90IDwtIA0KICAgIGdncHVicjo6Z2dwYWlyZWQoZGZfbW9kaWYsIHggPSAiUHJlUG9zdCIsIHkgPSAidmFsdWUiLCBpZCA9IGluZCwgDQogICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJQcmVQb3N0IiwgbGluZS5jb2xvciA9ICJncmF5IiwgbGluZS5zaXplID0gMC40LA0KICAgICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiMwMEFGQkIiLCAiI0ZDNEUwNyIpLCBsZWdlbmQgPSAibm9uZSIsDQogICAgICAgICAgICAgICAgICAgICBmYWNldC5ieSA9IGZhY2V0LCBuY29sID0gMywgDQogICAgICAgICAgICAgICAgICAgICB4bGFiID0geGxhYikgKw0KICAgIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2UsICBjb2xvdXIgPSAiZGFya3JlZCIpICsNCiAgICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAidC50ZXN0IiwgcGFpcmVkID0gVFJVRSwgbGFiZWwueCA9IGFzLm51bWVyaWMoZGZfbW9kaWYkUHJlUG9zdCktMC40LCBsYWJlbC55ID0gbWF4KGRmX21vZGlmJHZhbHVlKSsxKSArIA0KICAgIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLCBwYWlyZWQgPSBUUlVFLCBsYWJlbCA9ICJwLnNpZ25pZiIsIGNvbXBhcmlzb25zID0gbGlzdChjKHByZV92YXIsIHBvc3RfdmFyKSkpDQogIA0KICBwcmludChzdGF0X2NvbXApDQogIGNhdCgiXG4iKSAgICAgICAgICAgICAgICAgICAgICANCiAgcHJpbnQocGxvdCkNCiAgY2F0KCJcbiIpDQogIHBsb3QubmV3KCkgICAgICAgICAgICAgICAgICAgICAjIE5lZWQgdGhpcyB3b3JrYXJvdW5kIGZvciBpbnRlcmxlYXZpbmcgdGFibGVzIGFuZCBwbG90cyBpbiBSIE1hcmtkb3duLCB3aXRoaW4gbG9vcA0KICBkZXYub2ZmKCkNCn0NCmBgYA0KDQoNCmBgYHtyIGRlZl9mdW5jX2hlYXRjb3JwbG90bHksIGhpZGU9VFJVRSwgcmVzdWx0cz0nYXNpcyd9DQpoZWF0X2Nvcl9wbG90bHkgPC0gZnVuY3Rpb24oZGYsIHhfdmFycyA9IE5VTEwsIHlfdmFycyA9IE5VTEwsIGxvd19jb2xvciA9ICJjeWFuIiwgIGhpZ2hfY29sb3IgPSAicmVkIiwgIC4uLil7ICAgDQogICMgaW5oZXJpdCB0eXBlID0gYygicGVhcnNvbiIsInNwZWFybWFuIikgZnJvbSBIbWlzYzo6cmNvcnIoKSANCiAgbGlicmFyeShnZ3Bsb3QyKQ0KICBsaWJyYXJ5KHBsb3RseSkNCiAgbGlicmFyeShyZXNoYXBlMikNCiAgbGlicmFyeShIbWlzYykNCiAgDQogICMgdXNlIGFsbCBudW1lcmljIGNvbHVtbnMgb25seSwgcHJpbnQgbWVzc2FnZSBpZiBub24tbnVtZXJpYyBhcmUgZm91bmQNCiAgbnVtZXJpY19jb2xzIDwtIHVubGlzdChsYXBwbHkoZGYsIGlzLm51bWVyaWMpKQ0KICBpZighYWxsKG51bWVyaWNfY29scykpIG1lc3NhZ2UoIldhcm5pbmc6IE5vbi1udW1lcmljIGNvbHVtbnMgd2VyZSBleGNsdWRlZCEiKQ0KICBkZiA8LSBkZlssIG51bWVyaWNfY29sc10NCiAgDQogIGRmX21hdCA8LSBhcy5tYXRyaXgoZGYpDQogIHJ0IDwtIEhtaXNjOjpyY29ycihkZl9tYXQsIC4uLikNCiAgDQogICMgZXh0cmFjdCBjb3JyZWxhdGlvbnMsIHAtdmFsdWVzIGFuZCBtZXJnZSBpbnRvIGFub3RoZXIgZGF0YWZyYW1lDQogIG10bHIgPC0gcmVzaGFwZTI6Om1lbHQocnQkciwgdmFsdWUubmFtZSA9ICJDb3JyZWxhdGlvbiIpDQogIG10bHAgPC0gcmVzaGFwZTI6Om1lbHQocnQkUCwgdmFsdWUubmFtZSA9ICJQLVZhbHVlIikNCiAgDQogIG10bCA8LSBtZXJnZShtdGxyLCBtdGxwKQ0KICANCiAgIyBnaXZlIHBvc3NpYmlsaXR5IHRvIHBydW5lIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgNCiAgaWYoIWlzLm51bGwoeF92YXJzKSl7DQogICAgbXRsIDwtIG10bFsobXRsJFZhcjEgJWluJSB4X3ZhcnMpLCBdDQogIH0NCiAgaWYoIWlzLm51bGwoeF92YXJzKSl7DQogICAgbXRsIDwtIG10bFsobXRsJFZhcjIgJWluJSB5X3ZhcnMpLCBdDQogIH0NCiAgDQogICMgd2FudCB0byBhdm9pZCBzY2llbnRpZmljIG5vdGV0aW9uLCBidXQgdGhpcyBkb2VzbnQgd29yayBhcyBudW1lcmljDQogICMgbXRsJENvcnJlbGF0aW9uIDwtIGFzLm51bWVyaWMoZm9ybWF0KG10bCRDb3JyZWxhdGlvbiwgZGlnaXRzID0gNCwgc2NpZW50aWZpYyA9IEZBTFNFKSkgICMgZG9lc250IHdvcmsNCiAgIyBtdGwkYFAtVmFsdWVgIDwtIGFzLm51bWVyaWMoZm9ybWF0KG10bCRgUC1WYWx1ZWAsIGRpZ2l0cyA9IDQsIHNjaWVudGlmaWMgPSBGQUxTRSkpIA0KICBvcHRpb25zKHNjaXBlbiA9IDk5OSkNCiAgbXRsJENvcnJlbGF0aW9uIDwtIHJvdW5kKG10bCRDb3JyZWxhdGlvbiwgMykNCiAgbXRsJGBQLVZhbHVlYCA8LSByb3VuZChtdGwkYFAtVmFsdWVgLCAzKQ0KDQogIGd4IDwtDQogICAgZ2dwbG90Mjo6Z2dwbG90KG10bCwgDQogICAgICAgICAgIGFlcyhWYXIxLCBWYXIyLCANCiAgICAgICAgICAgICAgIGZpbGwgPSBDb3JyZWxhdGlvbiwgIA0KICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlKCJQLXZhbCA9ICIsIGBQLVZhbHVlYCkpKSArDQogICAgZ2dwbG90Mjo6Z2VvbV90aWxlKCkgKyANCiAgICBnZ3Bsb3QyOjpzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9IGxvd19jb2xvciwgIGhpZ2ggPSBoaWdoX2NvbG9yLCBsaW1pdHMgPSBjKC0xLCAxKSwgYnJlYWtzID0gYygtMSwgLS41LCAwLCAuNSwgMSkpICsNCiAgICBnZ3Bsb3QyOjp0aGVtZV9taW5pbWFsKCkgKw0KICAgIHtpZihhbnkobmNoYXIobmFtZXMoZGYpKSA+IDYpKSBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKX0gICMgdmVydGljYWwgeCBheGlzIGxhYmVscyBpZiBsZW5naHR5DQogIHBsb3RseTo6Z2dwbG90bHkoZ3gpICANCn0NCmBgYA0KDQoNCiMgUGxvdCBBZ2UNCg0KYGBge3IgcGxvdDEsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIHJlc3VsdHM9J2FzaXMnfQ0KIyMgRG9kZ2VkIEJhciBwbG90IG9mIEFnZSBhbmQgR2VuZGVyDQpEYXRhICAlPiUNCiAgbXV0YXRlKFZhcnRhX2NhdGVnID0gY3V0KFZhcnN0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3M9YygtSW5mLCAyNSwgMzAsIDM1LCA0MCwgNDUsIDUwLCA1NSwgNjAsIEluZiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIjwyNSIsIjI1LTI5IiwiMzAtMzQiLCAiMzUtMzkiLCAiNDAtNDQiLCAiNDUtNDkiLCAiNTAtNTQiLCAiNTUtNTkiLCAiNjA+IiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcmlnaHQgPSBGQUxTRSkpICU+JSAgDQogIG11dGF0ZShWYXJzdGEgPSBhcy5mYWN0b3IoVmFyc3RhKSwNCiAgICAgICAgIEdlbiA9IGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIoR2VuKSkpICU+JQ0KICBtdXRhdGUoR2VuID0gZm9yY2F0czo6ZmN0X3JlY29kZShHZW4sICJmZW1pbiIgPSAiMSIsICJtYXNjdWxpbiIgPSAiMiIpKSAlPiUNCiAgZHBseXI6OmNvdW50KFZhcnRhX2NhdGVnLCBHZW4sIC5kcm9wID0gRkFMU0UpICU+JSAgICAgICAgICMgR3JvdXAgYnksIHRoZW4gY291bnQgbnVtYmVyIGluIGVhY2ggZ3JvdXAgKGRvbnQgZHJvcCAwIGNvdW50cykNCiAgbXV0YXRlKHBjdCA9IHByb3AudGFibGUobikpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQ2FsY3VsYXRlIHBlcmNlbnQgd2l0aGluIGVhY2ggdmFyDQogICAgZ2dwbG90KGFlcyh4ID0gVmFydGFfY2F0ZWcsIHkgPSBwY3QsIGZpbGwgPSBHZW4sIGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KHBjdCkpKSArIA0KICAgICAgZ2VvbV9jb2wocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZShwcmVzZXJ2ZSA9ICJzaW5nbGUiKSwgc3RhdCA9ICJpZGVudGl0eSIsKSArICAgICMgRG9uJ3QgZHJvcCB6ZXJvIGNvdW50DQogICAgICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IC45KSwgICAgICAjIG1vdmUgdG8gY2VudGVyIG9mIGJhcnMNCiAgICAgICAgICAgICAgICB2anVzdCA9IC0wLjUsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbnVkZ2UgYWJvdmUgdG9wIG9mIGJhcg0KICAgICAgICAgICAgICAgIHNpemUgPSAzKSArIA0KICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICAgICAgZ2d0aXRsZSgiIikgKw0KICAgICAgeGxhYigiVmFyc3RhIikgKyB5bGFiKCJQZXJjZW50YWdlICUiKSArIA0KICAgICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiR2VuIiwgbmNvbCA9IDEpKSArIA0KICAgICAgc2NhbGVfZmlsbF9ncmV5KHN0YXJ0ID0gMC44LCBlbmQgPSAwLjIsIG5hLnZhbHVlID0gInJlZCIsIGFlc3RoZXRpY3MgPSAiZmlsbCIpICsNCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLCANCiAgICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLCAxKSwgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3VyID0gImJsYWNrIikpDQpgYGANCg0KDQojIyBCeSBQcm90b2NvbA0KDQpgYGB7ciBwbG90MV8yLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0yOCwgcmVzdWx0cz0nYXNpcyd9DQojIyBEb2RnZWQgQmFyIHBsb3Qgb2YgQWdlIGFuZCBHZW5kZXIgYnkgUHJvdG9jb2wNCkRhdGEgICU+JQ0KICBtdXRhdGUoVmFydGFfY2F0ZWcgPSBjdXQoVmFyc3RhLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcz1jKC1JbmYsIDI1LCAzMCwgMzUsIDQwLCA0NSwgNTAsIDU1LCA2MCwgSW5mKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiPDI1IiwiMjUtMjkiLCIzMC0zNCIsICIzNS0zOSIsICI0MC00NCIsICI0NS00OSIsICI1MC01NCIsICI1NS01OSIsICI2MD4iKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICByaWdodCA9IEZBTFNFKSkgJT4lICANCiAgbXV0YXRlKFZhcnN0YSA9IGFzLmZhY3RvcihWYXJzdGEpLA0KICAgICAgICAgR2VuID0gYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihHZW4pKSkgJT4lDQogIG11dGF0ZShHZW4gPSBmb3JjYXRzOjpmY3RfcmVjb2RlKEdlbiwgImZlbWluIiA9ICIxIiwgIm1hc2N1bGluIiA9ICIyIikpICU+JQ0KICBncm91cF9ieShQKSAlPiUNCiAgZHBseXI6OmNvdW50KFZhcnRhX2NhdGVnLCBHZW4sIC5kcm9wID0gRkFMU0UpICU+JSAgICAgICAgICMgR3JvdXAgYnksIHRoZW4gY291bnQgbnVtYmVyIGluIGVhY2ggZ3JvdXAgKGRvbnQgZHJvcCAwIGNvdW50cykNCiAgbXV0YXRlKHBjdCA9IHByb3AudGFibGUobikpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQ2FsY3VsYXRlIHBlcmNlbnQgd2l0aGluIGVhY2ggdmFyDQogICAgZ2dwbG90KGFlcyh4ID0gVmFydGFfY2F0ZWcsIHkgPSBwY3QsIGZpbGwgPSBHZW4sIGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KHBjdCkpKSArDQogICAgICBmYWNldF93cmFwKH5QLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2wgPSAxKSArDQogICAgICBnZW9tX2NvbChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHByZXNlcnZlID0gInNpbmdsZSIpLCBzdGF0ID0gImlkZW50aXR5IiwpICsgICAgIyBEb24ndCBkcm9wIHplcm8gY291bnQNCiAgICAgIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gLjkpLCAgICAgICMgbW92ZSB0byBjZW50ZXIgb2YgYmFycw0KICAgICAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBudWRnZSBhYm92ZSB0b3Agb2YgYmFyDQogICAgICAgICAgICAgICAgc2l6ZSA9IDMpICsgDQogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogICAgICBnZ3RpdGxlKCIiKSArDQogICAgICB4bGFiKCJWYXJzdGEiKSArIHlsYWIoIlBlcmNlbnRhZ2UgJSIpICsgDQogICAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJHZW4iLCBuY29sID0gMSkpICsgDQogICAgICBzY2FsZV9maWxsX2dyZXkoc3RhcnQgPSAwLjgsIGVuZCA9IDAuMiwgbmEudmFsdWUgPSAicmVkIiwgYWVzdGhldGljcyA9ICJmaWxsIikgKw0KICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsIA0KICAgICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAsIDEpLCBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvdXIgPSAiYmxhY2siKSkNCmBgYA0KDQoNCmBgYHtyIHBsb3QyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02LCByZXN1bHRzPSdhc2lzJ30NCiMjIFBpZSBjaGFydA0KRGF0YSAgJT4lDQogIG11dGF0ZShHZW4gPSBhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKEdlbikpKSAlPiUNCiAgbXV0YXRlKEdlbiA9IGZvcmNhdHM6OmZjdF9yZWNvZGUoR2VuLCAiZmVtaW4iID0gIjEiLCAibWFzY3VsaW4iID0gIjIiKSkgJT4lDQogIGdyb3VwX2J5KEdlbikgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnRzID0gbigpKSAlPiUNCiAgbXV0YXRlKHByb3AgPSByb3VuZChjb3VudHMqMTAwL3N1bShjb3VudHMpLCAxKSwNCiAgICAgICAgIGxhYi55cG9zID0gY3Vtc3VtKHByb3ApIC0gLjUqcHJvcCwNCiAgICAgICAgIFBlcmNlbnQgPSBwYXN0ZTAocHJvcCwgIiAlIikpICU+JSANCiAgZ2dwdWJyOjpnZ3BpZSh4ID0gInByb3AiLCBsYWJlbCA9ICJQZXJjZW50IiwNCiAgICAgICAgICAgICAgICBmaWxsID0gIkdlbiIsIGNvbG9yID0gIndoaXRlIiwgDQogICAgICAgICAgICAgICAgbGFiLnBvcyA9ICJpbiIsIGxhYi5mb250ID0gbGlzdChjb2xvciA9ICJ3aGl0ZSIpLA0KICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAiZ3JleSIpDQpgYGANCg0KDQoNCiMgQW5hbHlzZXMNCg0KIyMgU2ltcGxlIGJlZm9yZS1hZnRlciBhbmFseXNlcyB3aXRoIHQgdGVzdA0KDQpgYGB7ciB0X3Rlc3QxLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD02LCByZXN1bHRzPSdhc2lzJ30NCiMjIFNpbXBsZSBiZWZvcmUtYWZ0ZXIgYW5hbHlzZXMgd2l0aCB0IHRlc3QNCmNhdCgiIyMjIyBWQVMgU3RyZXNzIikNCmZ1bmNfdF9ib3goRGF0YSwgaW5kID0gIklEIiwgIlN0cmVzX3ByZSIsICJTdHJlc19wb3N0IiwgZmFjZXQgPSBGQUxTRSkgDQpgYGANCg0KDQpgYGB7ciB0X3Rlc3QyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0xMiwgcmVzdWx0cz0nYXNpcyd9DQojIyBTaW1wbGUgYmVmb3JlLWFmdGVyIGFuYWx5c2VzIHdpdGggdCB0ZXN0DQpjYXQoIiMjIyMgVkFTIFN0cmVzcyIpDQpmdW5jX3RfYm94KERhdGEsIGluZCA9ICJJRCIsICJTdHJlc19wcmUiLCAiU3RyZXNfcG9zdCIsIGZhY2V0ID0gVFJVRSkgDQpgYGANCg0KDQojIyBDb3JyZWxhdGlvbnM6IEFub3RpbXB1cmkgLSBQZXJzb25hbGl0eSAod2l0aG91dCBQNiwgUDcpDQoNCmBgYHtyIGNvcl9hbm9wZXJzLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9NywgcmVzdWx0cz0nYXNpcyd9DQpkYXRlcGxvdF9hbm9wZXJzIDwtIERhdGFbLCBjKCJQIiwgIlByaW1hdmFyYSIsICJWYXJhIiwgIlRvYW1uYSIsICJJYXJuYSIpXQ0KZGF0ZXBsb3RfYW5vcGVycyA8LSBjYmluZChkYXRlcGxvdF9hbm9wZXJzLCBEYXRhWywgODc6MTIxXSkNCmRhdGVwbG90X2Fub3BlcnMgPC0gc3Vic2V0KGRhdGVwbG90X2Fub3BlcnMsIFAhPTYgJiBQIT03KQ0KDQpDT1IgPC0gSG1pc2M6OnJjb3JyKGFzLm1hdHJpeChkYXRlcGxvdF9hbm9wZXJzWywgLTFdKSkgICANCk0gPC0gQ09SJHJbMTo0LCBdDQpQX01BVCA8LSBDT1IkUFsxOjQsIF0NCmNvcnJwbG90Ojpjb3JycGxvdChNLCB0eXBlID0gInVwcGVyIiwgcC5tYXQgPSBQX01BVCwgc2lnLmxldmVsID0gMC4wNSwgaW5zaWcgPSAiYmxhbmsiLCB0bC5jb2wgPSAiYmxhY2siLCB0bC5jZXggPSAuNywgY2wucG9zID0gImIiLCB0bC5zcnQgPSA0NSkNCiMgY29ycnBsb3Q6OmNvcnJwbG90KE0sIG1ldGhvZCA9ICJudW1iZXIiLCB0eXBlID0gInVwcGVyIiwgcC5tYXQgPSBQX01BVCwgc2lnLmxldmVsID0gMC4wNSwgaW5zaWcgPSAiYmxhbmsiLCB0bC5jb2wgPSAiYmxhY2siLCB0bC5jZXggPSAuNSwgdGwuc3J0ID0gNDUpICANCmBgYA0KDQojIyBDb3JyZWxhdGlvbnM6IEFub3RpbXB1cmkgLSBDYWxpdGF0ZSBBbWludGlyaSAod2l0aG91dCBQNiwgUDcpDQoNCmBgYHtyIGNvcjEsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTksIHJlc3VsdHM9J2FzaXMnfQ0KZGF0ZXBsb3QxIDwtIERhdGFbLCBjKCJQIiwgIlByaW1hdmFyYSIsICJWYXJhIiwgIlRvYW1uYSIsICJJYXJuYSIsICJNZWRpYV9zMSIsICJNZWRpYV9zMiIsICJNZWRpYV9zMyIsICAiU29jRGloX1BhcnQiLCAgIlNvY0RpaF9GYW1OIiwgICJTb2NEaWhfRmFtSW5kIiwgICJTb2NEaWhfUHJpZXQiLCAgIlNvY0RpaF9BbWljaSIsICAiU29jRGloX05lY3VuIiwgICJTb2NEaWhfQW50YWciLCAgIlNvY0RpaF9Ub3RBcHJvcCIsICAiU29jRGloX1RvdE5lYXByb3AiLCAiU1RBSV9UIildIA0KbmFtZXMoZGF0ZXBsb3QxKSA8LSBjKCJQIiwgIlByaW1hdmFyYSIsICJWYXJhIiwgIlRvYW1uYSIsICJJYXJuYSIsICJTMS0gVmFsZW50YSIsICJTMiAtIFZpdmlkbmVzcyIsICJTMyAtIFJlbGV2YW50YSIsICAiUGFydGVuZXIiLCAgIkZhbWlsaWUgbnVjbGV1IiwgICJGYW1pbGllIGV4dGluc2EiLCAgIlByaWV0ZW5pIiwgICJBbWljaSIsICAiTmVjdW5vc2N1dGkiLCAgIkFudGFnb25pc3RpIiwgICJUb3RpIEFwcm9waWF0aWkiLCAgIlRvdGkgTmVhcHJvcGlhdGlpIiwgIlNUQUlfVCIpDQpkYXRlcGxvdDEgPC0gc3Vic2V0KGRhdGVwbG90MSwgUCE9NiAmIFAhPTcpDQoNCkNPUiA8LSBIbWlzYzo6cmNvcnIoYXMubWF0cml4KGRhdGVwbG90MVssLTFdKSkgICANCk0gPC0gQ09SJHINClBfTUFUIDwtIENPUiRQDQpjb3JycGxvdDo6Y29ycnBsb3QoTSwgbWV0aG9kID0gIm51bWJlciIsIHR5cGUgPSAidXBwZXIiLCBwLm1hdCA9IFBfTUFULCBzaWcubGV2ZWwgPSAwLjA1LCBpbnNpZyA9ICJibGFuayIsIHRsLmNvbCA9ICJibGFjayIsIHRsLmNleCA9IC45LCB0bC5zcnQgPSA0NSkgIA0KYGBgDQoNCg0KYGBge3IgaGVhdF9jb3IxLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD05LCByZXN1bHRzPSdhc2lzJ30NCmhlYXRfY29yX3Bsb3RseShkYXRlcGxvdDFbLC0xXSkNCmBgYA0KDQoNCiMjIENvcnJlbGF0aW9uczogUGVyc29uYWxpdHkgLSBRdWFsaXRpZXMgb2YgTWVtb3JpZXMgKHdpdGhvdXQgUDYsIFA3KQ0KDQpgYGB7ciBjb3IyLCBmaWcud2lkdGg9MTEsIGZpZy5oZWlnaHQ9MTEsIHJlc3VsdHM9J2FzaXMnfQ0KZGF0ZXBsb3QyIDwtIERhdGFbLCBjKDI0LCA0MCwgNTYsIDg3OjEyMSwgMTI2KV0gDQpuYW1lcyhkYXRlcGxvdDIpWzE6M10gPC0gYygiUzEtIFZhbGVudGEiLCAiUzIgLSBWaXZpZG5lc3MiLCAiUzMgLSBSZWxldmFudGEiKQ0KDQpDT1IgPC0gSG1pc2M6OnJjb3JyKGFzLm1hdHJpeChkYXRlcGxvdDIpKSAgIA0KTSA8LSBDT1Ikcg0KUF9NQVQgPC0gQ09SJFANCmNvcnJwbG90Ojpjb3JycGxvdChNLCB0eXBlID0gInVwcGVyIiwgcC5tYXQgPSBQX01BVCwgc2lnLmxldmVsID0gMC4wNSwgaW5zaWcgPSAiYmxhbmsiLCB0bC5jb2wgPSAiYmxhY2siLCB0bC5jZXggPSAuNywgY2wucG9zID0gImIiLCB0bC5zcnQgPSA0NSkNCmBgYA0KDQoNCmBgYHtyIGhlYXRfY29yMiwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9MTIsIHJlc3VsdHM9J2FzaXMnfQ0KaGVhdF9jb3JfcGxvdGx5KGRhdGVwbG90MiwgeF92YXJzID0gbmFtZXMoZGF0ZXBsb3QyKVsxOjNdLCB5X3ZhcnMgPSBuYW1lcyhkYXRlcGxvdDIpWy0oMTozKV0pDQpgYGANCg0KDQojIyBDb3JyZWxhdGlvbnM6IFNvY2lhbCAtIFBlcnNvbmFsaXR5DQoNCmBgYHtyIGNvcjMsIGZpZy53aWR0aD0xMSwgZmlnLmhlaWdodD0xMSwgcmVzdWx0cz0nYXNpcyd9DQpkYXRlcGxvdDMgPC0gRGF0YVssIGMoMTMxOjEzOSwgODc6MTIxKV0NCm5hbWVzKGRhdGVwbG90MylbMTo5XSA8LSBjKCJQYXJ0ZW5lciIsICAiRmFtaWxpZSBudWNsZXUiLCAgIkZhbWlsaWUgZXh0aW5zYSIsICAiUHJpZXRlbmkiLCAgIkFtaWNpIiwgICJOZWN1bm9zY3V0aSIsICAiQW50YWdvbmlzdGkiLCAgIlRvdGkgQXByb3BpYXRpaSIsICAiVG90aSBOZWFwcm9waWF0aWkiKQ0KDQpDT1IgPC0gSG1pc2M6OnJjb3JyKGFzLm1hdHJpeChkYXRlcGxvdDMpKSAgIA0KTSA8LSBDT1Ikcg0KUF9NQVQgPC0gQ09SJFANCmNvcnJwbG90Ojpjb3JycGxvdChNLCB0eXBlID0gInVwcGVyIiwgcC5tYXQgPSBQX01BVCwgc2lnLmxldmVsID0gMC4wNSwgaW5zaWcgPSAiYmxhbmsiLCB0bC5jb2wgPSAiYmxhY2siLCB0bC5jZXggPSAuNywgY2wucG9zID0gImIiLCB0bC5zcnQgPSA0NSkNCmBgYA0KDQoNCmBgYHtyIGhlYXRfY29yMywgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MTIsIHJlc3VsdHM9J2FzaXMnfQ0KaGVhdF9jb3JfcGxvdGx5KGRhdGVwbG90MywgeF92YXJzID0gbmFtZXMoZGF0ZXBsb3QzKVsxOjldLCB5X3ZhcnMgPSBuYW1lcyhkYXRlcGxvdDMpWy0oMTo5KV0pDQpgYGANCg0KDQojIFNvY2lhbA0KDQpgYGB7ciBzb2NfMSwgcmVzdWx0cz0nYXNpcycsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTksIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMgbmFtZXMoRGF0YVtzdHJfZGV0ZWN0KGNvbG5hbWVzKERhdGEpLCBmaXhlZCgiU29jRGloIiwgaWdub3JlX2Nhc2U9VFJVRSkpXSkNCkRhdGFfc29jIDwtDQogIERhdGEgJT4lDQogIGRwbHlyOjpzZWxlY3QoIklEIiwgIlAiLCBzdGFydHNfd2l0aCgiU29jRGloIikpICU+JQ0KICBoYXZlbjo6emFwX2Zvcm1hdHMoLikgJT4lICAgICAgICAgICAgICAgICMgdG8gbm90IGdldCB3YXJuaW5nICANCiAgaGF2ZW46OnphcF9sYWJlbHMoLikgJT4lICAgICAgICAgICAgICAgICAjICJhdHRyaWJ1dGVzIGFyZSBub3QgaWRlbnRpY2FsIGFjcm9zcyBtZWFzdXJlIHZhcmlhYmxlcyINCiAgaGF2ZW46OnphcF93aWR0aHMoLikgJT4lICAgICAgICAgICAgICAgICAjIG9uIGdhdGhlcigpIGJlY2F1c2Ugb2YgU1BTUw0KICBkcGx5cjo6cmVuYW1lX2FsbChsaXN0KH5zdHJpbmdyOjpzdHJfcmVwbGFjZSguLCAiU29jRGloXyIsICIiKSkpICU+JSANCiAgZ2F0aGVyKGtleSA9ICJWYXJpYWJsZSIsIHZhbHVlID0gIlZhbHVlIiwgLUlELCAtUCkgDQogIA0KIyBDcmVhdGUgYSBjdXN0b20gY29sb3Igc2NhbGUgZm9yIGFsbCBBU0NRIGdyYXBocw0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpteUNvbG9ycyA8LSBicmV3ZXIucGFsKDEwLCJTZXQxIikNCm5hbWVzKG15Q29sb3JzKSA8LSBsZXZlbHMoRGF0YV9zb2MkVmFyaWFibGUpDQpjb2xTY2FsZSA8LSBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWUgPSAiVmFyaWFibGUiLCB2YWx1ZXMgPSBteUNvbG9ycykNCg0KIyBQbG90DQpnZ3B1YnI6OmdndmlvbGluKGRhdGEgPSBEYXRhX3NvYywgeCA9ICJWYXJpYWJsZSIsIHkgPSAiVmFsdWUiLCBmaWxsID0gIlZhcmlhYmxlIiwNCiAgICAgIGFkZCA9ICJib3hwbG90IiwgYWRkLnBhcmFtcyA9IGxpc3QoZmlsbCA9ICJ3aGl0ZSIpLA0KICAgICAgeGxhYiA9ICIiLCBsZWdlbmQgPSAibm9uZSIpICsNCiAgY29sU2NhbGUgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY29sb3Igc2NhbGUgaGVyZSBrZWVwIGNvbnNpc3RlbmN5IG9mIGNvbG9yIHdpdGggZmFjdG9yIGxldmVsDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2UsICBjb2xvdXIgPSAiZGFya3JlZCIpICsgICAgICAgICANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkNCmBgYA0KDQoNCmBgYHtyIHNvY18yLCByZXN1bHRzPSdhc2lzJywgZmlnLmhlaWdodD0yOCwgZmlnLndpZHRoPTksIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMgUGxvdCBmYWNldGVkIGJ5IFByb3RvY29sDQpwIDwtIA0KICBnZ3B1YnI6OmdndmlvbGluKGRhdGEgPSBEYXRhX3NvYywgeCA9ICJWYXJpYWJsZSIsIHkgPSAiVmFsdWUiLCBmaWxsID0gIlZhcmlhYmxlIiwNCiAgICAgICAgYWRkID0gImJveHBsb3QiLCBhZGQucGFyYW1zID0gbGlzdChmaWxsID0gIndoaXRlIiksDQogICAgICAgIHhsYWIgPSAiIiwgbGVnZW5kID0gIm5vbmUiKSArDQogICAgICAgICANCiAgICBjb2xTY2FsZSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjb2xvciBzY2FsZSBoZXJlIGtlZXAgY29uc2lzdGVuY3kgb2YgY29sb3Igd2l0aCBmYWN0b3IgbGV2ZWwNCiAgICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCAgY29sb3VyID0gImRhcmtyZWQiKSArICAgICAgICAgDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkNCg0KZ2dwdWJyOjpmYWNldChwLCBmYWNldC5ieSA9ICJQIiwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3giKQ0KYGBgDQoNCg0KIyMgU29jaWFsIC0gdGhpcyBkb2VzbnQgbWFrZSBzZW5zZSANCg0KYGBge3Igc29jXzMsIHJlc3VsdHM9J2FzaXMnLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04LCBmaWcuYWxpZ249J2NlbnRlcid9DQojIFBlcmZvcm1hbmNlQW5hbHl0aWNzOjpjaGFydC5Db3JyZWxhdGlvbihEYXRhWywgYyg4LCAxMzEsIDEzMiwgMTMzLCAxMzQsIDEzNSwgMTM2LCAxMzcsIDEzOCwgMTM5LCAxNDApXSkNCg0KIyBFeGFtcGxlIG9mIFNpbXBzb25zIFBhcmFkb3gNCmNvcGxvdChEaWZTdHJlcyB+IFNvY0RpaF9BbWljaSB8IE1lZGlhX3MxLA0KICAgICAgIGRhdGEgPSBEYXRhLA0KICAgICAgIHJvd3MgPSAxLA0KICAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgew0KICAgICAgICAgIHBhbmVsLnNtb290aCh4LCB5LCBzcGFuID0gLjgsIGl0ZXIgPSA1LC4uLikNCiAgICAgICAgICBhYmxpbmUobG0oeSB+IHgpLCBjb2wgPSAiYmx1ZSIpfSkNCmBgYA0KDQoNCiMgVmFyc3RhIEFtaW50IC0gUDEsUDIsUDMNCg0KYGBge3IgZGlmc3RyZXNfdmFyc3RhYW1pbnQsIHJlc3VsdHM9J2FzaXMnLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04LCBmaWcuYWxpZ249J2NlbnRlcid9DQpEYXRhX1AxUDJQMyA8LSANCiAgRGF0YSAlPiUNCiAgZmlsdGVyKFAgJWluJSBjKCIxIiwgIjIiLCAiMyIpKSAlPiUNCiAgbXV0YXRlKE1lZF9hbWludHZhcnN0YSA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKE1lZF9hbWludHZhcnN0YSkpLA0KICAgICAgICAgRGlmX01lZF9hbWludHZhcnN0YSA9IFZhcnN0YSAtIE1lZF9hbWludHZhcnN0YSkNCg0KUGVyZm9ybWFuY2VBbmFseXRpY3M6OmNoYXJ0LkNvcnJlbGF0aW9uKERhdGFfUDFQMlAzWywgYyg4LCA4NSwgMTIyLCAxNDgpXSkNCg0KY29wbG90KERpZlN0cmVzIH4gRGlmX01lZF9hbWludHZhcnN0YSB8IE1lZGlhX3MxLA0KICAgICAgIGRhdGEgPSBEYXRhX1AxUDJQMywNCiAgICAgICByb3dzID0gMSwNCiAgICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsNCiAgICAgICAgICBwYW5lbC5zbW9vdGgoeCwgeSwgc3BhbiA9IC44LCBpdGVyID0gNSwuLi4pDQogICAgICAgICAgYWJsaW5lKGxtKHkgfiB4KSwgY29sID0gImJsdWUiKX0pDQpgYGANCg0KDQojIFByb3RvY29sIDMgLSBTb2NpYWwNCg0KYGBge3IgcDNfc29jLCByZXN1bHRzPSdhc2lzJywgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OCwgZmlnLmFsaWduPSdjZW50ZXInfQ0KRGF0YV9QMyA8LSANCiAgRGF0YSAlPiUNCiAgZmlsdGVyKFAgPT0gIjMiKQ0KDQpQZXJmb3JtYW5jZUFuYWx5dGljczo6Y2hhcnQuQ29ycmVsYXRpb24oRGF0YV9QM1ssIGMoOCwgMTMxLCAxMzIsIDEzMywgMTM0LCAxMzUsIDEzNiwgMTM3LCAxMzgsIDEzOSwgMTQwKV0pDQoNCiMgY29wbG90KFNvY0RpaF9BbWljaSB+IERpZlN0cmVzIHwgTWVkaWFfczEsIA0KIyAgICAgICAgZGF0YSA9IERhdGEsDQojICAgICAgICBjb2x1bW5zID0gMywNCiMgICAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgew0KIyAgICAgICAgICAgcGFuZWwuc21vb3RoKHgsIHksIHNwYW4gPSAuOCwgaXRlciA9IDUsLi4uKQ0KIyAgICAgICAgICAgYWJsaW5lKGxtKHkgfiB4KSwgY29sID0gImJsdWUiKSB9ICkNCmBgYA0KDQoNCiMgUHJvdG9jb2wgMyAtIFZhcnN0YSBBbWludA0KDQpgYGB7ciBwM19kaWZzdHJlc192YXJzdGFhbWludCwgcmVzdWx0cz0nYXNpcycsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTgsIGZpZy5hbGlnbj0nY2VudGVyJ30NCkRhdGFfUDMgPC0gDQogIERhdGEgJT4lDQogIGZpbHRlcihQID09ICIzIikgJT4lDQogIG11dGF0ZShNZWRfYW1pbnR2YXJzdGEgPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihNZWRfYW1pbnR2YXJzdGEpKSwNCiAgICAgICAgIERpZl9NZWRfYW1pbnR2YXJzdGEgPSBWYXJzdGEgLSBNZWRfYW1pbnR2YXJzdGEpDQoNClBlcmZvcm1hbmNlQW5hbHl0aWNzOjpjaGFydC5Db3JyZWxhdGlvbihEYXRhX1AzWywgYyg4LCA4NSwgMTIyLCAxNDgpXSkNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KPGJyPg0KDQoNCg0KDQoNCjwhLS0gU2Vzc2lvbiBJbmZvIGFuZCBMaWNlbnNlIC0tPg0KDQo8YnI+DQoNCiMgU2Vzc2lvbiBJbmZvDQpgYGB7ciBzZXNzaW9uX2luZm8sIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9ICdtYXJrdXAnfQ0Kc2Vzc2lvbkluZm8oKSAgICANCmBgYA0KDQo8IS0tIEZvb3RlciAtLT4NCiZuYnNwOw0KPGhyIC8+DQo8cCBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+QSB3b3JrIGJ5IDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9DbGF1ZGl1UGFwYXN0ZXJpLyI+Q2xhdWRpdSBQYXBhc3Rlcmk8L2E+PC9wPg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIHN0eWxlPSJjb2xvcjogIzgwODA4MDsiPjxlbT5jbGF1ZGl1LnBhcGFzdGVyaUBnbWFpbC5jb208L2VtPjwvc3Bhbj48L3A+DQombmJzcDsNCg==